﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Engine.Graphics;
using Engine;
using Game;
using Jint.Native;

namespace MFTrain
{ 
    public class MFRailBlock : Block
    {
        public const int Index = 543;

        public BoundingBox[][] boundingBoxes = new BoundingBox[10][];
        public BoundingBox[] breakerBoxes = new BoundingBox[6];
        public BlockMesh[] m_blockMeshes = new BlockMesh[10]; 
        public Texture2D texture;
        public Texture2D trainTexture;
        public BlockMesh[] trainBreakerMeshes = new BlockMesh[6];

        /*制动器type:
         * 0,1:XY方向
         * 2,3:135度与45度
         * 4,5:斜铁轨的XY方向
         */

        public override void Initialize()
        {
            base.Initialize(); 
            var mesh = new BlockMesh();
            texture = ContentManager.Get<Texture2D>("MF/Train/Blocks");

            trainTexture = ContentManager.Get<Texture2D>("MF/Train/Train");
            Model breakerModel = ContentManager.Get<Model>("MF/Train/TrainBreaker");
            Matrix boneAbsoluteTransform = BlockMesh.GetBoneAbsoluteTransform(breakerModel.FindMesh("TrainBreaker").ParentBone);
            //水平XY
            trainBreakerMeshes[0] = new BlockMesh();
            trainBreakerMeshes[0].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[0].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[1] = new BlockMesh();
            trainBreakerMeshes[1].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(-0.5f, 0f, 0.5f) * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(90), 0, 0), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[1].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(-0.5f, 0f, 0.5f) * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(90), 0, 0), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            //转弯
            trainBreakerMeshes[2] = new BlockMesh();
            trainBreakerMeshes[2].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(135), 0, 0) * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[2].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(135), 0, 0) * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[3] = new BlockMesh();
            trainBreakerMeshes[3].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(45), 0, 0) * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[3].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(45), 0, 0) * Matrix.CreateTranslation(0.5f, 0f, 0.5f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            //爬坡
            trainBreakerMeshes[4] = new BlockMesh();
            trainBreakerMeshes[4].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(0.5f, 0.5f, 0.5f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[4].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(0.5f, 0.5f, 0.5f), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[5] = new BlockMesh();
            trainBreakerMeshes[5].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(-0.5f, 0.5f, 0.5f) * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(90), 0, 0), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            trainBreakerMeshes[5].AppendModelMeshPart(breakerModel.FindMesh("TrainBreaker").MeshParts[0], boneAbsoluteTransform * Matrix.CreateTranslation(-0.5f, 0.5f, 0.5f) * Matrix.CreateFromYawPitchRoll(MathUtils.DegToRad(90), 0, 0), makeEmissive: false, flipWindingOrder: true, doubleSided: false, flipNormals: false, Color.White);

            var vertices = new[]
            {
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0, 0.0625f),
                    Position = new Vector3(0, 0.01f, 1)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0.0625f, 0.0625f),
                    Position = new Vector3(1, 0.01f, 1)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0.0625f, 0),
                    Position = new Vector3(1, 0.01f, 0)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0, 0),
                    Position = new Vector3(0, 0.01f, 0)
                }
            };
            var indices = new ushort[] { 2, 1, 0, 0, 3, 2 };
            mesh.Vertices.AddRange(vertices);
            mesh.Indices.AddRange(indices);

            var flatMesh = new BlockMesh();
            flatMesh.AppendBlockMesh(mesh);
            flatMesh.TransformTextureCoordinates(Matrix.CreateTranslation(new Vector3(238 % 16 / 16f, 238 / 16 / 16f, 0f)));

            var center = Matrix.CreateTranslation(new Vector3(0.5f, 0, 0.5f));
            var reverseCenter = Matrix.CreateTranslation(new Vector3(-0.5f, 0, -0.5f));

            for (int i = 0; i < 4; i++)
            {
                m_blockMeshes[i] = new BlockMesh();
                m_blockMeshes[i].AppendBlockMesh(flatMesh);
                m_blockMeshes[i].TransformPositions(reverseCenter * Matrix.CreateRotationY(MathUtils.PI * 0.5f * i) * center);
                m_blockMeshes[i].GenerateSidesData();
            }

            flatMesh = new BlockMesh();
            flatMesh.AppendBlockMesh(mesh);
            flatMesh.TransformTextureCoordinates(Matrix.CreateTranslation(new Vector3(237 % 16 / 16f, 237 / 16 / 16f, 0f)));

            m_blockMeshes[4] = new BlockMesh();
            m_blockMeshes[4].AppendBlockMesh(flatMesh);
            m_blockMeshes[4].GenerateSidesData();

            m_blockMeshes[5] = new BlockMesh();
            m_blockMeshes[5].AppendBlockMesh(flatMesh);
            m_blockMeshes[5].TransformPositions(reverseCenter * Matrix.CreateRotationY(MathUtils.PI * 0.5f) * center);
            m_blockMeshes[5].GenerateSidesData();

            mesh = new BlockMesh();
            vertices = new BlockMeshVertex[]
            {
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0, 0.0625f),
                    Position = new Vector3(0, 0.01f, 1)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0.0625f, 0.0625f),
                    Position = new Vector3(1, 0.01f, 1)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0.0625f, 0),
                    Position = new Vector3(1, 1.01f, 0)
                },
                new BlockMeshVertex
                {
                    Color = Color.White,
                    TextureCoordinates = new Vector2(0, 0),
                    Position = new Vector3(0, 1.01f, 0)
                }
            };
            indices = new ushort[] { 2, 1, 0, 0, 1, 2, 0, 3, 2, 2, 3, 0 };
            mesh.Vertices.AddRange(vertices);
            mesh.Indices.AddRange(indices);
            mesh.TransformTextureCoordinates(Matrix.CreateTranslation(new Vector3(237 % 16 / 16f, 237 / 16 / 16f, 0f)));

            for (int i = 6; i < 10; i++)
            {
                m_blockMeshes[i] = new BlockMesh();
                m_blockMeshes[i].AppendBlockMesh(mesh);
                m_blockMeshes[i].TransformPositions(reverseCenter * Matrix.CreateRotationY(MathUtils.PI * 0.5f * i) * center);
            }

            var boxes = new[]
            {
                new BoundingBox(0, 0, 0, 1, 0.01f, 1)
            };
            for (int i = 0; i < 6; i++)
                boundingBoxes[i] = boxes;
            boundingBoxes[6] = new[]
            {
                new BoundingBox(0, 0, 0, 1, 0.01f, 1),
                new BoundingBox(0, 0, 0.25f, 1, 0.25f, 1),
                new BoundingBox(0, 0, 0.5f, 1, 0.5f, 1),
                new BoundingBox(0, 0, 0.75f, 1, 0.75f, 1)
            };
            boundingBoxes[7] = new[]
            {
                new BoundingBox(0, 0, 0, 1, 0.01f, 1),
                new BoundingBox(0.25f, 0, 0, 1, 0.25f, 1),
                new BoundingBox(0.5f, 0, 0, 1, 0.5f, 1),
                new BoundingBox(0.75f, 0, 0, 1, 0.75f, 1)
            };
            boundingBoxes[8] = new[]
            {
                new BoundingBox(0, 0, 0, 1, 0.01f, 1),
                new BoundingBox(0, 0, 0, 1, 0.25f, 0.75f),
                new BoundingBox(0, 0, 0, 1, 0.5f, 0.5f),
                new BoundingBox(0, 0, 0, 1, 0.75f, 0.25f)
            };
            boundingBoxes[9] = new[]
            {
                new BoundingBox(0, 0, 0, 1, 0.01f, 1),
                new BoundingBox(0, 0, 0, 0.75f, 0.25f, 1),
                new BoundingBox(0, 0, 0, 0.5f, 0.5f, 1),
                new BoundingBox(0, 0, 0, 0.25f, 0.75f, 1)
            };

            breakerBoxes[0] = new BoundingBox(0, 0, 0.4f, 1, 0.5f, 0.6f);
            breakerBoxes[1] = new BoundingBox(0.4f, 0, 0, 0.6f, 0.5f, 1);
            breakerBoxes[2] = new BoundingBox(0, 0, 0, 1, 0.5f, 1);
            breakerBoxes[3] = new BoundingBox(0, 0, 0, 1, 0.5f, 1);
            breakerBoxes[4] = new BoundingBox(0, 0.5f, 0.4f, 1, 1, 0.6f);
            breakerBoxes[5] = new BoundingBox(0.4f, 0.5f, 0, 0.6f, 1, 1);
        }

        public override BoundingBox[] GetCustomCollisionBoxes(SubsystemTerrain terrain, int value)
        {
            return IsBreaker(Terrain.ExtractData(value)) ? 
                boundingBoxes[GetRailType(Terrain.ExtractData(value))].Concat(new BoundingBox[] { breakerBoxes[GetBreakerType(Terrain.ExtractData(value))] }).ToArray() : 
                boundingBoxes[GetRailType(Terrain.ExtractData(value))];
        } 

        public override void DrawBlock(PrimitivesRenderer3D primitivesRenderer, int value, Color color, float size, ref Matrix matrix, DrawBlockEnvironmentData environmentData)
        {
            BlocksManager.DrawFlatOrImageExtrusionBlock(primitivesRenderer, value, size, ref matrix, texture, color, false, environmentData);
        }

        public override void GenerateTerrainVertices(BlockGeometryGenerator generator, TerrainGeometry geometry, int value, int x, int y, int z)
        {
            generator.GenerateMeshVertices(this, x, y, z, m_blockMeshes[GetRailType(Terrain.ExtractData(value))], Color.White, null, geometry.GetGeometry(texture).SubsetAlphaTest);
            if (IsBreaker(Terrain.ExtractData(value)))
            {
                generator.GenerateMeshVertices(this, x, y, z, trainBreakerMeshes[GetBreakerType(Terrain.ExtractData(value))], Color.White, null, geometry.GetGeometry(trainTexture).SubsetOpaque);
            }
        }

        public static int GetRailType(int data)
        {
            return data & 15;//取前四位
        }

        public static int SetRailType(int data, int type)
        {
            return (data & -16) | (type & 15);//用data后10位并上type前四位
        }

        public static bool IsCorner(int type)
        {
            return (type >> 2) == 0;//第三位为0，角落轨道(0~3)
        }

        public static bool IsDirectionX(int type)
        {
            return (type & 1) == 0;
        }

        public static bool IsBreaker(int data)
        {
            return ((data >> 6) & 1) == 1;//data第六位是1，则变拦车器铁轨
        }

        public static bool IsHorizontal(int type)
        {
            return type == 4 || type == 5;
        }

        public static int SetBreaker(int data)
        {
            return data | 0b1000000;
        }

        public static int ClearBreaker(int data)
        {
            return data & 0b11111110111111;
        }

        /// <summary>
        /// 制动器type:
        ///  0,1:XY方向 
        ///  2,3:135度与45度
        ///  4,5:斜铁轨的XY方向
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static int GetBreakerType(int data)
        {
            if (IsHorizontal(GetRailType(data))) return IsDirectionX(GetRailType(data)) ? 0 : 1;
            else if (IsCorner(GetRailType(data))) return IsDirectionX(GetRailType(data)) ? 2 : 3;
            else return IsDirectionX(GetRailType(data)) ? 4 : 5;
        }

        public override BlockPlacementData GetPlacementValue(SubsystemTerrain subsystemTerrain, ComponentMiner componentMiner, int value, TerrainRaycastResult raycastResult)
        {
            Vector3 forward = componentMiner.ComponentCreature.ComponentCreatureModel.EyeRotation.ToYawPitchRoll();
            float pitch = MathUtils.Abs(MathUtils.RadToDeg(forward.X));
            return new BlockPlacementData
            {
                CellFace = raycastResult.CellFace,
                Value = Terrain.MakeBlockValue(Index, 0, SetRailType(0, (pitch > 45 && pitch < 135) ? 5 : 4))
            };
        }

        public override void GetDropValues(SubsystemTerrain subsystemTerrain, int oldValue, int newValue, int toolLevel, List<BlockDropValue> dropValues, out bool showDebris)
        {
            showDebris = true;
            dropValues.Add(new BlockDropValue { Value = Index, Count = 1 });
        }

        public static bool CanConnectTo(int railType, int direction)
        {
            if (IsCorner(railType))
                return direction == railType || direction == ((railType + 1) & 3);
            return IsDirectionX(railType) ^ !IsDirectionX(direction);
        }
    }
}
